import { useState, useContext } from 'react'
import { useDrop } from 'react-dnd'
import QuickBtns from '../components/QuickBtns'
import ProcessEditorContext from '../context'
import FLOWABLE from "../FLOWABLE";

const DesignPanel = () => {

  const {
    editorManager,
    containmentRules,
    getStencilItemById
  } = useContext(ProcessEditorContext)

  const [dragCurrentParent, setDragCurrentParent] = useState(undefined)
  const [dragCurrentParentId, setDragCurrentParentId] = useState(undefined)
  const [dragCurrentParentStencil, setDragCurrentParentStencil] = useState(undefined)
  const [dropTargetElement, setDropTargetElement] = useState(undefined)
  const [dragCanContain, setDragCanContain] = useState(undefined)

  const handleDrop = (item, pos, type) => {
    const { id } = item
    editorManager.handleEvents({
      type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
      highlightId: "shapeRepo.attached"
    });
    editorManager.handleEvents({
      type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
      highlightId: "shapeRepo.added"
    });
    editorManager.handleEvents({
      type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
      highlightId: "shapeMenu"
    });
    FLOWABLE.eventBus.dispatch(FLOWABLE.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS);

    if (dragCanContain) {
      var additionalIEZoom = 1;
      var screenCTM = editorManager.getCanvas().node.getScreenCTM();
      // Correcting the UpperLeft-Offset
      pos.x -= (screenCTM.e / additionalIEZoom);
      pos.y -= (screenCTM.f / additionalIEZoom);
      // Correcting the Zoom-Factor
      pos.x /= screenCTM.a;
      pos.y /= screenCTM.d;

      // Correcting the ScrollOffset
      pos.x -= document.documentElement.scrollLeft;
      pos.y -= document.documentElement.scrollTop;

      var parentAbs = dragCurrentParent.absoluteXY();
      pos.x -= parentAbs.x;
      pos.y -= parentAbs.y;


      var containedStencil = undefined;
      var stencilSets = editorManager.getStencilSets().values();

      for (var i = 0; i < stencilSets.length; i++) {
        var stencilSet = stencilSets[i];
        var nodes = stencilSet.nodes();
        for (var j = 0; j < nodes.length; j++) {
          if (nodes[j].idWithoutNs() === id) {
            containedStencil = nodes[j];
            break;
          }
        }

        if (!containedStencil) {
          var edges = stencilSet.edges();
          for (var j = 0; j < edges.length; j++) {
            if (edges[j].idWithoutNs() === id) {
              containedStencil = edges[j];
              break;
            }
          }
        }
      }

      if (!containedStencil) return;

      if (type === 'quick') {
        var shapes = editorManager.getSelection();
        if (shapes && shapes.length == 1) {
          var currentSelectedShape = shapes.first();
          var option = {};
          option.type = currentSelectedShape.getStencil().namespace() + item.id;
          option.namespace = currentSelectedShape.getStencil().namespace();
          option.connectedShape = currentSelectedShape;
          option.parent = dragCurrentParent;
          option.containedStencil = containedStencil;

          // if (!e.ctrlKey) {
          // Get the center of the shape
          var cShape = currentSelectedShape.bounds.center();
          // Snapp +-20 Pixel horizontal to the center 
          if (20 > Math.abs(cShape.x - pos.x)) {
            pos.x = cShape.x;
          }
          // Snapp +-20 Pixel vertical to the center 
          if (20 > Math.abs(cShape.y - pos.y)) {
            pos.y = cShape.y;
          }
          // }

          option.position = pos;
          const notAllowStecils = ['SequenceFlow', 'Association', 'MessageFlow', 'DataAssociation']
          if (!notAllowStecils.includes(containedStencil.idWithoutNs())) {
            const args = {
              sourceShape: currentSelectedShape,
              targetStencil: containedStencil
            };
            const targetStencil = editorManager.getRules().connectMorph(args);
            if (!targetStencil) return
            option.connectingType = targetStencil.id();
          }
          const command = new FLOWABLE.CreateCommand(option, dropTargetElement, pos, editorManager.getEditor());
          editorManager.executeCommands([command]);
        }
      }
      else if (type === 'palette') {
        var canAttach = [
          'BoundaryErrorEvent', 'BoundaryTimerEvent', 'BoundarySignalEvent',
          'BoundaryMessageEvent', 'BoundaryMessageEvent', 'BoundaryCancelEvent', 'BoundaryCompensationEvent'
        ].includes(containedStencil.idWithoutNs());

        var option = {};
        const modelData = editorManager.getBaseModelData();
        option['type'] = modelData.model.stencilset.namespace + item.id;
        option['namespace'] = modelData.model.stencilset.namespace;
        option['position'] = pos;
        option['parent'] = dragCurrentParent;

        const command = new FLOWABLE.CreatePalette(option, dragCurrentParent, canAttach, pos, editorManager.getEditor());
        editorManager.executeCommands([command]);

        // Fire event to all who want to know about this
        var dropEvent = {
          type: FLOWABLE.eventBus.EVENT_TYPE_ITEM_DROPPED,
          droppedItem: item,
          position: pos
        };
        FLOWABLE.eventBus.dispatch(dropEvent.type, dropEvent);
      }
    }
    setDragCurrentParent(undefined)
    setDragCurrentParentId(undefined)
    setDragCurrentParentStencil(undefined)
    setDropTargetElement(undefined)
    setDragCanContain(undefined)
  }

  const handleDragQuick = (stencil, pos) => {
    if (isOver) {
      var coord = editorManager.eventCoordinatesXY(pos.x, pos.y);
      var additionalIEZoom = 1;
      if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
        var ua = navigator.userAgent;
        if (ua.indexOf('MSIE') >= 0) {
          //IE 10 and below
          var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
          if (zoom !== 100) {
            additionalIEZoom = zoom / 100
          }
        }
      }
      if (additionalIEZoom !== 1) {
        coord.x = coord.x / additionalIEZoom;
        coord.y = coord.y / additionalIEZoom;
      }
      // 获取drop点覆盖的元素的个数
      var aShapes = editorManager.getCanvas().getAbstractShapesAtPosition(coord);

      if (aShapes.length <= 0) {
        setDragCanContain(false)
        return false
      }
      if (aShapes[0] instanceof ORYX.Core.Canvas) {
        editorManager.getCanvas().setHightlightStateBasedOnX(coord.x);
      }

      var id = stencil.id

      var stencil = undefined;
      var stencilSets = editorManager.getStencilSets().values();
      for (var i = 0; i < stencilSets.length; i++) {
        var stencilSet = stencilSets[i];
        var nodes = stencilSet.nodes();
        for (var j = 0; j < nodes.length; j++) {
          if (nodes[j].idWithoutNs() === id) {
            stencil = nodes[j];
            break;
          }
        }
        if (!stencil) {
          var edges = stencilSet.edges();
          for (var j = 0; j < edges.length; j++) {
            if (edges[j].idWithoutNs() === id) {
              stencil = edges[j];
              break;
            }
          }
        }
      }
      var candidate = aShapes.last();
      var isValid = false; // 标识显示红色高亮还是绿色高亮

      if (stencil.type() === "node") {
        // 拖拽节点快捷按钮
        var canContain = editorManager.getRules().canContain({
          containingShape: candidate,
          containedStencil: stencil
        });
        var parentCandidate = aShapes.reverse().find(function (candidate) {
          return (candidate instanceof ORYX.Core.Canvas
            || candidate instanceof ORYX.Core.Node
            || candidate instanceof ORYX.Core.Edge);
        });

        if (!parentCandidate) {
          setDragCanContain(false)
          return false
        }

        setDragCurrentParent(parentCandidate);
        setDragCurrentParentId(parentCandidate.id);
        setDragCurrentParentStencil(parentCandidate.getStencil().id());
        setDropTargetElement(parentCandidate);
        setDragCanContain(canContain);

        isValid = canContain;
      } else {
        // 拖拽连线快捷按钮
        var shapes = editorManager.getSelection();

        if (shapes && shapes.length == 1) {
          var currentSelectedShape = shapes.first();
          var curCan = candidate;
          var canConnect = false;

          var targetStencil = getStencilItemById(curCan.getStencil().idWithoutNs());

          if (targetStencil) {
            var associationConnect = false;
            if (
              stencil.idWithoutNs() === 'Association' &&
              (
                curCan.getStencil().idWithoutNs() === 'TextAnnotation' ||
                curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent'
              )
            ) {
              associationConnect = true;
            } else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore') {
              associationConnect = true;
            }

            if (targetStencil.canConnectTo || associationConnect) {
              while (!canConnect && curCan && !(curCan instanceof ORYX.Core.Canvas)) {
                candidate = curCan;
                //check connection rules

                canConnect = editorManager.getRules().canConnect({
                  sourceShape: currentSelectedShape,
                  edgeStencil: stencil,
                  targetShape: curCan
                });
                curCan = curCan.parent;
              }
            }
          }
          var parentCandidate = editorManager.getCanvas();

          isValid = canConnect;
          setDragCurrentParent(parentCandidate);
          setDragCurrentParentId(parentCandidate.id);
          setDragCurrentParentStencil(parentCandidate.getStencil().id());
          setDragCanContain(canConnect);
          setDropTargetElement(candidate);

        }
      }
      // 拖拽过程中悬浮在某个节点上显示高亮
      editorManager.handleEvents({
        type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
        highlightId: 'shapeMenu',
        elements: [candidate],
        color: isValid ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR
      });
    }
  }

  const handleDropQuick = (stencil, pos) => {
    handleDrop(stencil, pos, 'quick')
  }

  const handleDragPalette = (stencil, pos) => {
    if (isOver) {
      const { id } = stencil
      // 获取drop点在画布中的位置
      var coord = editorManager.eventCoordinatesXY(pos.x, pos.y);
      var additionalIEZoom = 1;
      if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
        var ua = navigator.userAgent;
        if (ua.indexOf('MSIE') >= 0) {
          //IE 10 and below
          var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
          if (zoom !== 100) {
            additionalIEZoom = zoom / 100
          }
        }
      }
      if (additionalIEZoom !== 1) {
        coord.x = coord.x / additionalIEZoom;
        coord.y = coord.y / additionalIEZoom;
      }

      // 获取drop点覆盖的元素的个数
      var aShapes = editorManager.getCanvas().getAbstractShapesAtPosition(coord);
      if (aShapes.length <= 0) {
        setDragCanContain(false)
        return false
      }
      if (aShapes[0] instanceof ORYX.Core.Canvas) {
        editorManager.getCanvas().setHightlightStateBasedOnX(coord.x);
      }
      // var item = getStencilItemById(id);
      // var item = stencil;
      if (aShapes.length == 1 && aShapes[0] instanceof ORYX.Core.Canvas) {
        var item = getStencilItemById(id);
        var parentCandidate = aShapes[0];

        var cants = [
          'Lane', 'BoundaryErrorEvent', 'BoundaryMessageEvent', 'BoundarySignalEvent',
          'BoundaryTimerEvent', 'BoundaryCancelEvent', 'BoundaryCompensationEvent', 'EntryCriterion'
        ];
        if (cants.includes(id)) {
          setDragCanContain(false)
          // Show Highlight
          editorManager.handleEvents({
            type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
            highlightId: 'shapeRepo.added',
            elements: [parentCandidate],
            style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE,
            color: ORYX.CONFIG.SELECTION_INVALID_COLOR
          });

        } else {
          setDragCurrentParent(parentCandidate)
          setDragCurrentParentId(parentCandidate.id)
          setDragCanContain(true)
          editorManager.handleEvents({
            type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
            highlightId: "shapeRepo.added"
          });
        }
        editorManager.handleEvents({
          type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
          highlightId: "shapeRepo.attached"
        });
        return false;
      } else {
        var item = getStencilItemById(id);
        var parentCandidate = aShapes.reverse().find(function (candidate) {
          return (candidate instanceof ORYX.Core.Canvas || candidate instanceof ORYX.Core.Node || candidate instanceof ORYX.Core.Edge);
        });

        if (!parentCandidate) {
          setDragCanContain(false)
          return false
        }

        if (item.type === "node") {
          var _canContain = false;
          var parentStencilId = parentCandidate.getStencil().id();
          if (dragCurrentParentId && dragCurrentParentId === parentCandidate.id) {
            return false;
          }
          var parentItem = getStencilItemById(parentCandidate.getStencil().idWithoutNs());

          if (parentItem.roles.indexOf('Activity') > -1) {
            if (item.roles.indexOf('IntermediateEventOnActivityBoundary') > -1
              || item.roles.indexOf('EntryCriterionOnItemBoundary') > -1
              || item.roles.indexOf('ExitCriterionOnItemBoundary') > -1) {
              _canContain = true;
            }
          } else if (parentItem.roles.indexOf('StageActivity') > -1) {
            if (item.roles.indexOf('EntryCriterionOnItemBoundary') > -1
              || item.roles.indexOf('ExitCriterionOnItemBoundary') > -1) {
              _canContain = true;
            }

          } else if (parentItem.roles.indexOf('StageModelActivity') > -1) {
            if (item.roles.indexOf('ExitCriterionOnItemBoundary') > -1) {
              _canContain = true;
            }
          } else if (parentCandidate.getStencil().idWithoutNs() === 'Pool') {
            if (item.id === 'Lane') {
              _canContain = true;
            }
          }

          if (_canContain) {
            editorManager.handleEvents({
              type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
              highlightId: "shapeRepo.attached",
              elements: [parentCandidate],
              style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE,
              color: ORYX.CONFIG.SELECTION_VALID_COLOR
            });

            editorManager.handleEvents({
              type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
              highlightId: "shapeRepo.added"
            });

          } else {
            for (var i = 0; i < containmentRules.length; i++) {
              var rule = containmentRules[i];
              if (rule.role === parentItem.id) {
                for (var j = 0; j < rule.contains.length; j++) {
                  if (item.roles.indexOf(rule.contains[j]) > -1) {
                    _canContain = true;
                    break;
                  }
                }

                if (_canContain) {
                  break;
                }
              }
            }
            // Show Highlight
            editorManager.handleEvents({
              type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
              highlightId: 'shapeRepo.added',
              elements: [parentCandidate],
              color: _canContain ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR
            });

            editorManager.handleEvents({
              type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
              highlightId: "shapeRepo.attached"
            });
          }

          setDragCurrentParent(parentCandidate)
          setDragCurrentParentId(parentCandidate.id)
          setDragCurrentParentStencil(parentStencilId)
          setDragCanContain(_canContain)
        } else {
          var canvasCandidate = editorManager.getCanvas();
          var canConnect = false;

          var targetStencil = getStencilItemById(parentCandidate.getStencil().idWithoutNs());
          if (targetStencil) {
            var associationConnect = false;
            if (stencil.idWithoutNs() === 'Association' && (curCan.getStencil().idWithoutNs() === 'TextAnnotation' || curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent')) {
              associationConnect = true;
            } else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore') {
              associationConnect = true;
            }

            if (targetStencil.canConnectTo || associationConnect) {
              canConnect = true;
            }
          }

          setDragCurrentParent(parentCandidate)
          setDragCurrentParentId(parentCandidate.id)
          setDragCurrentParentStencil(canvasCandidate.getStencil().id())
          setDragCanContain(canConnect)

          // Show Highlight
          editorManager.handleEvents({
            type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
            highlightId: 'shapeRepo.added',
            elements: [canvasCandidate],
            color: ORYX.CONFIG.SELECTION_VALID_COLOR
          });

          editorManager.handleEvents({
            type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
            highlightId: "shapeRepo.attached"
          });
        }
      }
    }
  }

  const handleDropPalette = (item, pos) => {
    handleDrop(item, pos, 'palette')
  }

  const [{ isOver }, drop] = useDrop({
    accept: 'PROCESS_DESIGNER',
    collect: (monitor) => {
      return {
        isOver: monitor.isOver(),
      };
    },
    hover(item, monitor) {
      const { x, y } = monitor.getClientOffset();
      if (item.dragType === 'quick') {
        handleDragQuick(item.stencil, { x, y })
      } else if (item.dragType === 'palette') {
        handleDragPalette(item.stencil, { x, y })
      }
    },
    drop(item, monitor) {
      const { x, y } = monitor.getClientOffset();
      if (item.dragType === 'quick') {
        handleDropQuick(item.stencil, { x, y })
      } else if (item.dragType === 'palette') {
        handleDropPalette(item.stencil, { x, y })
      }
    }
  })

  return <div id="canvasSection" ref={drop}>
    <QuickBtns></QuickBtns>
  </div>
}

export default DesignPanel